#include "bindings/bindings_file.h"
#include "helpers/exception_handler.h"
#include "helpers/factories.h"
#include "helpers/general.h"
#include "helpers/resource.h"
#include "rive/file.hpp"
#include "rive/viewmodel/runtime/viewmodel_runtime.hpp"
#include "rive/viewmodel/viewmodel.hpp"

namespace ohos_rive {
std::string NapiValueToString(napi_env env, napi_value value)
{
    if (value == nullptr) {
        return "";
    }

    size_t length = 0;
    napi_get_value_string_utf8(env, value, nullptr, 0, &length);

    std::string result;
    if (length > 0) {
        result.resize(length);
        napi_get_value_string_utf8(env, value, &result[0], length + 1, &length);
    }

    return result;
}

napi_value CreateNapiString(napi_env env, const std::string &str)
{
    napi_value result;
    napi_create_string_utf8(env, str.c_str(), str.length(), &result);
    return result;
}

// FILE
napi_value Napi_File_import(napi_env env, napi_callback_info info)
{
    size_t argc = 3;
    napi_value args[3];
    napi_value thisArg;
    void *data;

    napi_get_cb_info(env, info, &argc, args, &thisArg, &data);
    if (argc < 3) {
        LOGE("Wrong number of arguments");
        return nullptr;
    }

    void *arrayBufferData;
    size_t arrayBufferLength;
    napi_get_arraybuffer_info(env, args[0], &arrayBufferData, &arrayBufferLength);

    int32_t rendererType;
    napi_get_value_int32(env, args[1], &rendererType);
    int64_t fileAssetLoaderPtr;
    napi_get_value_int64(env, args[2], &fileAssetLoaderPtr);

    auto *assetLoader = reinterpret_cast<rive::FileAssetLoader *>(fileAssetLoaderPtr);
    auto filePtr = Import(env, reinterpret_cast<uint8_t *>(arrayBufferData), static_cast<int32_t>(arrayBufferLength),
                          static_cast<RendererType>(rendererType), assetLoader);

    napi_value result;
    napi_create_int64(env, filePtr, &result);
    return result;
}

static napi_value Napi_File_File_cppEnums(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value args[1];
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    if (argc < 1) {
        LOGE("Expected 1 argument");
        return nullptr;
    }

    int64_t ref;
    napi_get_value_int64(env, args[0], &ref);
    auto *file = reinterpret_cast<rive::File *>(ref);

    if (!file) {
        LOGE("Invalid file reference");
        return nullptr;
    }

    auto enumItems = file->enums();
    napi_value resultArray;
    napi_create_array_with_length(env, enumItems.size(), &resultArray);

    uint32_t enumIndex = 0;
    for (const auto &enumItem : enumItems) {
        // { name: string, values: string[] }
        napi_value enumObj;
        napi_create_object(env, &enumObj);

        auto enumName = MakeNapiString(env, enumItem->enumName());
        if (enumName.get()) {
            napi_set_named_property(env, enumObj, "name", enumName.get());
        }

        auto values = enumItem->values();
        napi_value valuesArray;
        napi_create_array_with_length(env, values.size(), &valuesArray);

        uint32_t valueIndex = 0;
        for (const auto &value : values) {
            auto valueName = MakeNapiString(env, value->key());
            if (valueName.get()) {
                napi_set_element(env, valuesArray, valueIndex, valueName.get());
                valueIndex++;
            }
        }

        napi_set_named_property(env, enumObj, "values", valuesArray);
        napi_set_element(env, resultArray, enumIndex, enumObj);
        enumIndex++;
    }

    return resultArray;
}

napi_value FileCppArtboardByName(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2];
    napi_value thisArg;
    void *data;

    napi_get_cb_info(env, info, &argc, args, &thisArg, &data);
    if (argc < 2) {
        LOGE("Wrong number of arguments");
        return nullptr;
    }

    int64_t fileRef;
    napi_get_value_int64(env, args[0], &fileRef);

    std::string name = NapiValueToString(env, args[1]);
    auto file = reinterpret_cast<rive::File *>(fileRef);
    auto artboard = file->artboardNamed(name);

    if (artboard != nullptr) {
        artboard->advance(0.0);
    }

    napi_value result;
    napi_create_int64(env, reinterpret_cast<int64_t>(artboard.release()), &result);
    return result;
}

napi_value FileCppDelete(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value args[1];
    napi_value thisArg;
    void *data;

    napi_get_cb_info(env, info, &argc, args, &thisArg, &data);

    if (argc < 1) {
        LOGE("Wrong number of arguments");
        return nullptr;
    }

    int64_t fileRef;
    napi_get_value_int64(env, args[0], &fileRef);
    auto file = reinterpret_cast<rive::File *>(fileRef);
    file->unref();

    napi_value result;
    napi_get_undefined(env, &result);
    return result;
}

napi_value FileCppArtboardCount(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value args[1];
    napi_value thisArg;
    void *data;

    napi_get_cb_info(env, info, &argc, args, &thisArg, &data);
    if (argc < 1) {
        LOGE("Wrong number of arguments");
        return nullptr;
    }

    int64_t fileRef;
    napi_get_value_int64(env, args[0], &fileRef);
    auto file = reinterpret_cast<rive::File *>(fileRef);
    int32_t count = static_cast<int32_t>(file->artboardCount());

    napi_value result;
    napi_create_int32(env, count, &result);
    return result;
}

napi_value FileCppArtboardByIndex(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2];
    napi_value thisArg;
    void *data;

    napi_get_cb_info(env, info, &argc, args, &thisArg, &data);
    if (argc < 2) {
        LOGE("Wrong number of arguments");
        return nullptr;
    }

    int64_t fileRef;
    napi_get_value_int64(env, args[0], &fileRef);
    int32_t index;
    napi_get_value_int32(env, args[1], &index);
    auto file = reinterpret_cast<rive::File *>(fileRef);
    auto artboard = file->artboardAt(index);

    if (artboard != nullptr) {
        artboard->advance(0.0);
    }
    napi_value result;
    napi_create_int64(env, reinterpret_cast<int64_t>(artboard.release()), &result);
    return result;
}

napi_value FileCppArtboardNameByIndex(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2];
    napi_value thisArg;
    void *data;

    napi_get_cb_info(env, info, &argc, args, &thisArg, &data);

    if (argc < 2) {
        LOGE("Wrong number of arguments");
        return nullptr;
    }

    int64_t fileRef;
    napi_get_value_int64(env, args[0], &fileRef);
    int32_t index;
    napi_get_value_int32(env, args[1], &index);

    auto file = reinterpret_cast<rive::File *>(fileRef);
    auto artboard = file->artboard(index);
    auto name = artboard->name();

    return CreateNapiString(env, name);
}

napi_value FileCppCreateBindableArtboardByName(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2];
    napi_value thisArg;
    void *data;

    napi_get_cb_info(env, info, &argc, args, &thisArg, &data);

    if (argc < 2) {
        LOGE("Wrong number of arguments");
        return nullptr;
    }

    int64_t fileRef;
    napi_get_value_int64(env, args[0], &fileRef);

    std::string name = NapiValueToString(env, args[1]);
    auto file = reinterpret_cast<rive::File *>(fileRef);
    auto bindableArtboard = file->bindableArtboardNamed(name);

    napi_value result;
    napi_create_int64(env, reinterpret_cast<int64_t>(bindableArtboard.release()), &result);
    return result;
}

napi_value FileCppCreateDefaultBindableArtboard(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value args[1];
    napi_value thisArg;
    void *data;

    napi_get_cb_info(env, info, &argc, args, &thisArg, &data);

    if (argc < 1) {
        LOGE("Wrong number of arguments");
        return nullptr;
    }

    int64_t fileRef;
    napi_get_value_int64(env, args[0], &fileRef);

    auto file = reinterpret_cast<rive::File *>(fileRef);
    auto bindableArtboard = file->bindableArtboardDefault();

    napi_value result;
    napi_create_int64(env, reinterpret_cast<int64_t>(bindableArtboard.release()), &result);
    return result;
}

napi_value FileCppViewModelCount(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value args[1];
    napi_value thisArg;
    void *data;

    napi_get_cb_info(env, info, &argc, args, &thisArg, &data);

    if (argc < 1) {
        LOGE("Wrong number of arguments");
        return nullptr;
    }

    int64_t fileRef;
    napi_get_value_int64(env, args[0], &fileRef);

    auto file = reinterpret_cast<rive::File *>(fileRef);
    int32_t count = static_cast<int32_t>(file->viewModelCount());

    napi_value result;
    napi_create_int32(env, count, &result);
    return result;
}

napi_value FileCppViewModelByIndex(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2];
    napi_value thisArg;
    void *data;

    napi_get_cb_info(env, info, &argc, args, &thisArg, &data);

    if (argc < 2) {
        LOGE("Wrong number of arguments");
        return nullptr;
    }

    int64_t fileRef;
    napi_get_value_int64(env, args[0], &fileRef);

    int32_t index;
    napi_get_value_int32(env, args[1], &index);

    auto file = reinterpret_cast<rive::File *>(fileRef);
    auto viewModel = file->viewModelByIndex(index);

    napi_value result;
    napi_create_int64(env, reinterpret_cast<int64_t>(viewModel), &result);
    return result;
}

napi_value FileCppViewModelByName(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2];
    napi_value thisArg;
    void *data;

    napi_get_cb_info(env, info, &argc, args, &thisArg, &data);

    if (argc < 2) {
        LOGE("Wrong number of arguments");
        return nullptr;
    }

    int64_t fileRef;
    napi_get_value_int64(env, args[0], &fileRef);

    std::string name = NapiValueToString(env, args[1]);

    auto file = reinterpret_cast<rive::File *>(fileRef);
    auto viewModel = file->viewModelByName(name);

    napi_value result;
    napi_create_int64(env, reinterpret_cast<int64_t>(viewModel), &result);
    return result;
}

napi_value FileCppDefaultViewModelForArtboard(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2];
    napi_value thisArg;
    void *data;

    napi_get_cb_info(env, info, &argc, args, &thisArg, &data);

    if (argc < 2) {
        LOGE("Wrong number of arguments");
        return nullptr;
    }

    int64_t fileRef;
    napi_get_value_int64(env, args[0], &fileRef);

    int64_t artboardRef;
    napi_get_value_int64(env, args[1], &artboardRef);

    auto file = reinterpret_cast<rive::File *>(fileRef);
    auto artboard = reinterpret_cast<rive::Artboard *>(artboardRef);
    auto viewModel = file->defaultArtboardViewModel(artboard);

    napi_value result;
    napi_create_int64(env, reinterpret_cast<int64_t>(viewModel), &result);
    return result;
}
} // namespace ohos_rive
